import { forEach } from 'lodash-es';
import { BuilderOptions } from '../entity/builder-options';
import FarrisDesignEventEmitter from '../utils/event-emitter';

export class FarrisDesignBaseElement {

    /** 创建组件需要的参数，主要是存储一些组件的渲染方法  */
    public options: BuilderOptions;

    /** 创建组件时生成随机数，也可以在传入组件时指定 */
    public id: string;

    /** EventEmitter实例，方便事件的注册和发送 */
    public events: FarrisDesignEventEmitter;

    /** 存储事件列表，方便在组件销毁时将事件一同注销 */
    public eventHandlers: any[];

    get key(): string {
        return '';
    }

    constructor(options: BuilderOptions) {

        this.options = Object.assign({
            namespace: 'farris'
        }, options || {});

        this.id = `e${Math.random().toString(36).substring(7)}`;
        this.eventHandlers = [];
        this.events = (options && options.events) ? options.events : new FarrisDesignEventEmitter();
    }

    /**
     * 生成DOM结构
     * @param allParams
     * @returns html字符串 或者 HTMLElement
     */
    hook(...allParams: any[]) {
        const name = allParams[0];
        if (this.options && this.options.hooks && this.options.hooks[name]) {
            const result = this.options.hooks[name].apply(this, Array.prototype.slice.call(allParams, 1));
            // console.log(result);
            return result;
        } else {
            const fn = (typeof allParams[allParams.length - 1] === 'function') ? allParams[allParams.length - 1] : null;
            if (fn) {
                return fn(null, allParams[1]);
            } else {
                return allParams[1];
            }
        }
    }

    /**
     * 解析模板结构
     * @param rawTemplate 解析方法
     * @param data 控件json数据
     */
    interpolate(rawTemplate: any, data: any) {
        const evaluationData = Object.assign({
            instance: this,
            self: this
        }, data);

        if (typeof rawTemplate === 'function') {
            try {
                // console.log(evaluationData);

                const result = rawTemplate(evaluationData);
                // console.log(result);
                return result;

            } catch (err) {
                console.warn('Error interpolating template', err, data);
                return err.message;
            }
        }
    }

    /**
     * Emit a new event.
     *
     * @param   event - The event to emit.
     * @param  data - The data to emit with the handler.
     */
    emit(event, ...data) {
        if (this.events) {
            this.events.emit(`${this.options.namespace}.${event}`, data);

        }

    }

    /**
     * Wrapper method to add an event listener to an HTML element.
     *
     * @param obj
     *   The DOM element to add the event to.
     * @param type
     *   The event name to add.
     * @param func
     *   The callback function to be executed when the listener is triggered.
     * @param persistent
     *   If this listener should persist beyond 'destroy' commands.
     */
    addEventListener(obj, type, func, persistent = false) {
        if (!obj) {
            return;
        }
        if (!persistent) {
            this.eventHandlers.push({ id: this.id, obj, type, func });
        }
        if ('addEventListener' in obj) {
            obj.addEventListener(type, func, false);
        } else if ('attachEvent' in obj) {
            obj.attachEvent(`on${type}`, func);
        }

        return this;
    }


    on(event, cb) {
        if (!this.events) {
            return;
        }
        const type = `${this.options.namespace}.${event}`;

        // Store the component id in the handler so that we can determine which events are for this component.
        cb.id = this.id;
        cb.key = this.key;

        // Register for this event.
        return this.events.on(type, cb);
    }

    /**
     * Empty's an HTML DOM element.
     *
     * @param element - The element you wish to empty.
     */
    empty(element) {
        if (element) {
            while (element.firstChild) {
                element.removeChild(element.firstChild);
            }
        }
    }

    /**
     * Removes all event listeners attached to this component.
     */
    destroy() {
        this.removeEventListeners();
        this.removeAllEvents();
    }
    /**
     * 移除指定类型的事件
     * @param type 
     * @returns 
     */
    removeEventListener(type: string) {
        if (!this.eventHandlers) {
            return;
        }

        this.eventHandlers.forEach(handler => {
            if ((this.id === handler.id) && (type === handler.type) && handler.obj && handler.obj.removeEventListener) {
                handler.obj.removeEventListener(handler.type, handler.func);
                handler.obj.needRemove = true;
            }
        });
        this.eventHandlers = this.eventHandlers.filter(handler => handler.needRemove);
    }

    removeEventListeners() {
        this.eventHandlers.forEach(handler => {
            if ((this.id === handler.id) && handler.type && handler.obj && handler.obj.removeEventListener) {
                handler.obj.removeEventListener(handler.type, handler.func);
            }
        });
        this.eventHandlers = [];
    }

    removeAllEvents(includeExternal?: any) {
        forEach(this.events._events, (events, type) => {
            forEach(events, (listener) => {
                if (listener && (this.id === listener.id) && (includeExternal || listener.internal)) {
                    this.events.off(type, listener);
                }
            });
        });
    }
}
